package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	//在 Go 语言中，函数、map、切片 只支持与 nil 的比较，而不支持同类型两个变量的比较。 因此map的 key 不能使用 函数类型、map 类型、切片类型
	s1 := make([]int, 1)
	s2 := make([]int, 2)
	f1 := func() {}
	f2 := func() {}
	t1 := make(map[int]string)
	t2 := make(map[int]string)
	//println(s1 == s2) // 错误：invalid operation: s1 == s2 (slice can only be compared to nil)
	//println(f1 == f2) // 错误：invalid operation: f1 == f2 (func can only be compared to nil)
	//println(t1 == t2) // 错误：invalid operation: t1 == t2 (map can only be compared to nil)
	fmt.Println(s1, s2, f1, f2, t1, t2)

	//key作为接口类型时,也不能使用 函数、map、切片
	var badMap = map[interface{}]int{
		"1": 1,
		//[]int{2}: 2, //panic: runtime error: hash of unhashable type []int
		3: 3,
	}
	fmt.Println(badMap)

	//创建map
	var map1 map[int]string //没有初始化，值为 nil
	//对零值状态的 map 变量直接进行操作，就会导致运行时异常（panic）
	// map1[5] = "test"                //报错:panic: assignment to entry in nil map
	var map2 = make(map[int]string) //初始化
	// map2[5] = "test"                //不会报错
	var map3 = map[string]int{"Go": 98, "Python": 87, "Java": 79, "Html": 93}
	fmt.Println(map1) //map[]
	fmt.Println(map2) //map[]
	fmt.Println(map3) //map[Go:98 Html:93 Java:79 Python:87]

	fmt.Println(map1 == nil) //true
	fmt.Println(map2 == nil) //false
	fmt.Println(map3 == nil) //false

	//判断 nil map 则初始化
	//如果没有初始化就赋值，会报错 panic: assignment to entry in nil map
	if map1 == nil {
		map1 = make(map[int]string)
		fmt.Println(map1 == nil) //false
	}

	//或者使用短变量
	mx := map[string]int{}
	mx["key"] = 1
	fmt.Println(mx) //map[key:1]

	//稍微复杂一点的map初始化
	type Position struct {
		x float64
		y float64
	}
	m2 := map[Position]string{
		Position{29.935523, 52.568915}:  "school",
		Position{25.352594, 113.304361}: "shopping-mall",
		Position{73.224455, 111.804306}: "hospital",
	}
	fmt.Println("m2", m2) //map[{25.352594 113.304361}:shopping-mall {29.935523 52.568915}:school {73.224455 111.804306}:hospital]

	//上面的初始化方式可以简化一下:Go 允许省略字面值中的元素类型
	m3 := map[Position]string{
		{29.935523, 52.568915}:  "school",
		{25.352594, 113.304361}: "shopping-mall",
		{73.224455, 111.804306}: "hospital",
	}
	fmt.Println("m3", m3) //map[{25.352594 113.304361}:shopping-mall {29.935523 52.568915}:school {73.224455 111.804306}:hospital]

	//初始化并指定容量
	map4 := make(map[int]string, 8) // 指定初始容量为8
	fmt.Println("map4", map4)

	//存储键值对到map中
	map1[1] = "语文"
	map1[2] = "数学"
	map1[3] = "英语"
	map1[4] = "物理"
	map1[7] = ""
	fmt.Println(map1) //map[1:语文 2:数学 3:英语 4:物理 7:]

	//使用 for...range... 遍历map,打印结果无序
	for k, v := range map1 {
		fmt.Print(k, "=>", v, " ") //1=>语文 2=>数学 3=>英语 4=>物理 7=>
	}
	fmt.Println()

	//将map的key组成新的slice
	keys := make([]int, 0, len(map1))
	for k, _ := range map1 {
		keys = append(keys, k)
	}
	fmt.Println(keys) //[1 2 3 4 7]

	//根据key获取对应的value值
	fmt.Println(map1[4])  //物理
	fmt.Println(map1[40]) //""

	//在访问的 Key 不存在时，仍会返回零值，因此不能通过返回 nil 来判断元素是否存在
	v1, ok := map1[40]
	if ok {
		fmt.Println("对应的数值是：", v1)
	} else {
		fmt.Println("操作的key不存在")
	}

	//如果并不关心某个键对应的 value，而只关心某个键是否在于 map 中，可以使用空标识符替代变量 v
	_, ok = map1[40]
	fmt.Println(ok) //false

	//修改map的数据
	map1[3] = "历史"
	fmt.Println(map1) //map[1:语文 2:数学 3:历史 4:物理 7:]

	//删除map的数据
	delete(map1, 3)
	fmt.Println(map1) //map[1:语文 2:数学 4:物理 7:]

	//获取map的长度
	//map只有长度len,没有容量cap
	//注意:如果使用的map的key是int类型,建议优先使用slice,效率会比较高,内存占用比较少
	fmt.Println(len(map1)) //4

	//获取map的类型
	map11 := make(map[int]string)
	map12 := make(map[string]float64)
	fmt.Printf("%T\n", map11) //map[int]string
	fmt.Printf("%T\n", map12) //map[string]float64

	/*多个map组合成为slice,模拟返回给客户端的接口列表*/
	//创建多个map存储用户的信息
	user1 := make(map[string]string)
	user1["name"] = "张三"
	user1["age"] = "30"
	user1["sex"] = "男"
	user2 := map[string]string{"name": "李四", "age": "28", "sex": "女"}
	user3 := map[string]string{"name": "王五", "age": "21", "sex": "女"}
	//将map存入到slice中
	list := make([]map[string]string, 0, 3)
	list = append(list, user1)
	list = append(list, user2)
	list = append(list, user3)

	//遍历结果
	for i, val := range list {
		fmt.Printf("用户 %d：", i+1)
		fmt.Printf("姓名：%s,", val["name"])
		fmt.Printf("年龄：%s,", val["age"])
		fmt.Printf("性别：%s\n", val["sex"])
	}

	//将结果输出为json
	result, _ := json.Marshal(list)
	fmt.Println(string(result)) //[{"age":"30","name":"张三","sex":"男"},{"age":"28","name":"李四","sex":"女"},{"age":"21","name":"王五","sex":"女"}]

	/*二维结构*/
	maps := make(map[string]map[string]string)
	maps["user"] = map[string]string{"name": "zhangsan", "age": "30"}
	maps["fruit"] = map[string]string{"name": "apple", "price": "6"}
	res, _ := json.Marshal(maps)
	fmt.Println(string(res)) //{"fruit":{"name":"apple","price":"6"},"user":{"age":"30","name":"zhangsan"}}

	//map实现一个工厂模式,将map的值设置为一个方法
	m := map[int]func(op int) int{}
	m[1] = func(op int) int { return op }           //返回原值
	m[2] = func(op int) int { return op * op }      //计算平方
	m[3] = func(op int) int { return op * op * op } //计算立方
	fmt.Println(m[1](2))                            //2
	fmt.Println(m[2](2))                            //4
	fmt.Println(m[3](2))                            //8

	//map实现一个无序集合
	findNum := 1
	mySet := map[int]bool{}
	mySet[1] = true             //添加元素
	mySet[3] = true             //添加元素
	fmt.Println(mySet[findNum]) //判断元素是否存在:true
	fmt.Println(len(mySet))     //获取元素个数
	delete(mySet, 1)            //删除元素
	fmt.Println(mySet[findNum]) //false
}
